home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / Python 1.1 / Lib / dospath.py < prev    next >
Encoding:
Text File  |  1994-05-23  |  8.6 KB  |  355 lines  |  [TEXT/R*ch]

  1. # Module 'dospath' -- common operations on DOS pathnames
  2.  
  3. import os
  4. import stat
  5. import string
  6.  
  7.  
  8. # Normalize the case of a pathname.
  9. # On MS-DOS it maps the pathname to lowercase, turns slashes into
  10. # backslashes and maps invalid consecutive characters to a single '_'.
  11. # Other normalizations (such as optimizing '../' away) are not allowed
  12. # (this is done by normpath).
  13. #
  14. # Amrit: Things that can be valid regular expressions cannot be normalized
  15. #        away.  (which is pretty much all special characters)
  16. #
  17. #        I am assuming that at least these chars may be used:
  18. #                [, ], |, *, +, ?
  19.  
  20. mapchar = '_'
  21.  
  22. def normcase(s):
  23.     res, s = splitdrive(s)
  24.     for c in s:
  25.         if c in '/\\':
  26.             res = res + os.sep
  27.         elif c == '.' and res[-1:] == os.sep:
  28.             res = res + mapchar + c
  29.         elif ord(c) < 32 or c in ' ",:;<=>':
  30.             if res[-1:] != mapchar:
  31.                 res = res + mapchar
  32.         else:
  33.             res = res + c
  34.     return string.lower(res)
  35.  
  36.  
  37. # Return wheter a path is absolute.
  38. # Trivial in Posix, harder on the Mac or MS-DOS.
  39. # For DOS it is absolute if it starts with a slash or backslash (current
  40. # volume), or if a pathname after the volume letter and colon starts with
  41. # a slash or backslash.
  42.  
  43. def isabs(s):
  44.     s = splitdrive(s)[1]
  45.     return s != '' and s[:1] in '/\\'
  46.  
  47.  
  48. # Join two pathnames.
  49. # Ignore the first part if the second part is absolute.
  50. # Insert a '/' unless the first part is empty or already ends in '/'.
  51.  
  52. def join(a, b):
  53.     if isabs(b): return b
  54.     if a == '' or a[-1:] in '/\\': return a + b
  55.     # Note: join('x', '') returns 'x/'; is this what we want?
  56.     return a + os.sep + b
  57.  
  58.  
  59. # Split a path in a drive specification (a drive letter followed by a
  60. # colon) and the path specification.
  61. # It is always true that drivespec + pathspec == p
  62. def splitdrive(p):
  63.     if p[1:2] == ':':
  64.         return p[0:2], p[2:]
  65.     return '', p
  66.  
  67.  
  68. # Split a path in head (everything up to the last '/') and tail (the
  69. # rest).  If the original path ends in '/' but is not the root, this
  70. # '/' is stripped.  After the trailing '/' is stripped, the invariant
  71. # join(head, tail) == p holds.
  72. # The resulting head won't end in '/' unless it is the root.
  73.  
  74. def split(p):
  75.     d, p = splitdrive(p)
  76.     slashes = ''
  77.     while p and p[-1:] in '/\\':
  78.         slashes = slashes + p[-1]
  79.         p = p[:-1]
  80.     if p == '':
  81.         p = p + slashes
  82.     head, tail = '', ''
  83.     for c in p:
  84.         tail = tail + c
  85.         if c in '/\\':
  86.             head, tail = head + tail, ''
  87.     slashes = ''
  88.     while head and head[-1:] in '/\\':
  89.         slashes = slashes + head[-1]
  90.         head = head[:-1]
  91.     if head == '':
  92.         head = head + slashes
  93.     return d + head, tail
  94.  
  95.  
  96. # Split a path in root and extension.
  97. # The extension is everything starting at the first dot in the last
  98. # pathname component; the root is everything before that.
  99. # It is always true that root + ext == p.
  100.  
  101. def splitext(p):
  102.     root, ext = '', ''
  103.     for c in p:
  104.         if c in '/\\':
  105.             root, ext = root + ext + c, ''
  106.         elif c == '.' or ext:
  107.             ext = ext + c
  108.         else:
  109.             root = root + c
  110.     return root, ext
  111.  
  112.  
  113. # Return the tail (basename) part of a path.
  114.  
  115. def basename(p):
  116.     return split(p)[1]
  117.  
  118.  
  119. # Return the head (dirname) part of a path.
  120.  
  121. def dirname(p):
  122.     return split(p)[0]
  123.  
  124.  
  125. # Return the longest prefix of all list elements.
  126.  
  127. def commonprefix(m):
  128.     if not m: return ''
  129.     prefix = m[0]
  130.     for item in m:
  131.         for i in range(len(prefix)):
  132.             if prefix[:i+1] <> item[:i+1]:
  133.                 prefix = prefix[:i]
  134.                 if i == 0: return ''
  135.                 break
  136.     return prefix
  137.  
  138.  
  139. # Is a path a symbolic link?
  140. # This will always return false on systems where posix.lstat doesn't exist.
  141.  
  142. def islink(path):
  143.     return false
  144.  
  145.  
  146. # Does a path exist?
  147. # This is false for dangling symbolic links.
  148.  
  149. def exists(path):
  150.     try:
  151.         st = os.stat(path)
  152.     except os.error:
  153.         return 0
  154.     return 1
  155.  
  156.  
  157. # Is a path a dos directory?
  158. # This follows symbolic links, so both islink() and isdir() can be true
  159. # for the same path.
  160.  
  161. def isdir(path):
  162.     try:
  163.         st = os.stat(path)
  164.     except os.error:
  165.         return 0
  166.     return stat.S_ISDIR(st[stat.ST_MODE])
  167.  
  168.  
  169. # Is a path a regular file?
  170. # This follows symbolic links, so both islink() and isdir() can be true
  171. # for the same path.
  172.  
  173. def isfile(path):
  174.     try:
  175.         st = os.stat(path)
  176.     except os.error:
  177.         return 0
  178.     return stat.S_ISREG(st[stat.ST_MODE])
  179.  
  180.  
  181. # Are two filenames really pointing to the same file?
  182.  
  183. def samefile(f1, f2):
  184.     s1 = os.stat(f1)
  185.     s2 = os.stat(f2)
  186.     return samestat(s1, s2)
  187.  
  188.  
  189. # Are two open files really referencing the same file?
  190. # (Not necessarily the same file descriptor!)
  191. # XXX THIS IS BROKEN UNDER DOS! ST_INO seems to indicate number of reads?
  192.  
  193. def sameopenfile(fp1, fp2):
  194.     s1 = os.fstat(fp1.fileno())
  195.     s2 = os.fstat(fp2.fileno())
  196.     return samestat(s1, s2)
  197.  
  198.  
  199. # Are two stat buffers (obtained from stat, fstat or lstat)
  200. # describing the same file?
  201.  
  202. def samestat(s1, s2):
  203.     return s1[stat.ST_INO] == s2[stat.ST_INO] and \
  204.         s1[stat.ST_DEV] == s2[stat.ST_DEV]
  205.  
  206.  
  207. # Is a path a mount point?
  208. # XXX This degenerates in: 'is this the root?' on DOS
  209.  
  210. def ismount(path):
  211.     return isabs(splitdrive(path)[1])
  212.  
  213.  
  214. # Directory tree walk.
  215. # For each directory under top (including top itself, but excluding
  216. # '.' and '..'), func(arg, dirname, filenames) is called, where
  217. # dirname is the name of the directory and filenames is the list
  218. # files files (and subdirectories etc.) in the directory.
  219. # The func may modify the filenames list, to implement a filter,
  220. # or to impose a different order of visiting.
  221.  
  222. def walk(top, func, arg):
  223.     try:
  224.         names = os.listdir(top)
  225.     except os.error:
  226.         return
  227.     func(arg, top, names)
  228.     exceptions = ('.', '..')
  229.     for name in names:
  230.         if name not in exceptions:
  231.             name = join(top, name)
  232.             if isdir(name):
  233.                 walk(name, func, arg)
  234.  
  235.  
  236. # Expand paths beginning with '~' or '~user'.
  237. # '~' means $HOME; '~user' means that user's home directory.
  238. # If the path doesn't begin with '~', or if the user or $HOME is unknown,
  239. # the path is returned unchanged (leaving error reporting to whatever
  240. # function is called with the expanded path as argument).
  241. # See also module 'glob' for expansion of *, ? and [...] in pathnames.
  242. # (A function should also be defined to do full *sh-style environment
  243. # variable expansion.)
  244.  
  245. def expanduser(path):
  246.     if path[:1] <> '~':
  247.         return path
  248.     i, n = 1, len(path)
  249.     while i < n and path[i] not in '/\\':
  250.         i = i+1
  251.     if i == 1:
  252.         if not os.environ.has_key('HOME'):
  253.             return path
  254.         userhome = os.environ['HOME']
  255.     else:
  256.         return path
  257.     return userhome + path[i:]
  258.  
  259.  
  260. # Expand paths containing shell variable substitutions.
  261. # The following rules apply:
  262. #    - no expansion within single quotes
  263. #    - no escape character, except for '$$' which is translated into '$'
  264. #    - ${varname} is accepted.
  265. #    - varnames can be made out of letters, digits and the character '_'
  266. # XXX With COMMAND.COM you can use any characters in a variable name,
  267. # XXX except '^|<>='.
  268.  
  269. varchars = string.letters + string.digits + '_-'
  270.  
  271. def expandvars(path):
  272.     if '$' not in path:
  273.         return path
  274.     res = ''
  275.     index = 0
  276.     pathlen = len(path)
  277.     while index < pathlen:
  278.         c = path[index]
  279.         if c == '\'':    # no expansion within single quotes
  280.             path = path[index + 1:]
  281.             pathlen = len(path)
  282.             try:
  283.                 index = string.index(path, '\'')
  284.                 res = res + '\'' + path[:index + 1]
  285.             except string.index_error:
  286.                 res = res + path
  287.                 index = pathlen -1
  288.         elif c == '$':    # variable or '$$'
  289.             if path[index + 1:index + 2] == '$':
  290.                 res = res + c
  291.                 index = index + 1
  292.             elif path[index + 1:index + 2] == '{':
  293.                 path = path[index+2:]
  294.                 pathlen = len(path)
  295.                 try:
  296.                     index = string.index(path, '}')
  297.                     var = path[:index]
  298.                     if os.environ.has_key(var):
  299.                         res = res + os.environ[var]
  300.                 except string.index_error:
  301.                     res = res + path
  302.                     index = pathlen - 1
  303.             else:
  304.                 var = ''
  305.                 index = index + 1
  306.                 c = path[index:index + 1]
  307.                 while c != '' and c in varchars:
  308.                     var = var + c
  309.                     index = index + 1
  310.                     c = path[index:index + 1]
  311.                 if os.environ.has_key(var):
  312.                     res = res + os.environ[var]
  313.                 if c != '':
  314.                     res = res + c
  315.         else:
  316.             res = res + c
  317.         index = index + 1
  318.     return res
  319.  
  320.  
  321. # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
  322. # Also, components of the path are silently truncated to 8+3 notation.
  323.  
  324. def normpath(path):
  325.     path = normcase(path)
  326.     prefix, path = splitdrive(path)
  327.     while path[:1] == os.sep:
  328.         prefix = prefix + os.sep
  329.         path = path[1:]
  330.     comps = string.splitfields(path, os.sep)
  331.     i = 0
  332.     while i < len(comps):
  333.         if comps[i] == '.':
  334.             del comps[i]
  335.         elif comps[i] == '..' and i > 0 and \
  336.                       comps[i-1] not in ('', '..'):
  337.             del comps[i-1:i+1]
  338.             i = i-1
  339.         elif comps[i] == '' and i > 0 and comps[i-1] <> '':
  340.             del comps[i]
  341.         elif '.' in comps[i]:
  342.             comp = string.splitfields(comps[i], '.')
  343.             comps[i] = comp[0][:8] + '.' + comp[1][:3]
  344.             i = i+1
  345.         elif len(comps[i]) > 8:
  346.             comps[i] = comps[i][:8]
  347.             i = i+1
  348.         else:
  349.             i = i+1
  350.     # If the path is now empty, substitute '.'
  351.     if not prefix and not comps:
  352.         comps.append('.')
  353.     return prefix + string.joinfields(comps, os.sep)
  354.  
  355.